// 在 first commit 那次提交中，MASTER_ACCEPTS_THEN_DISTRIBUTES 是无法正确响应的，当时的正确实现是在 master-b.js 中的
// 在 second commit 中，是将 master-b.js 和 worker-b.js 合并到了 master.js 和 worker.js 中
/**
 * 在 third commit 中，增加了一半线程监听 3000 端口，一半线程监听 3001 端口
 * 以下情况为在 MASTER_ACCEPTS_THEN_DISTRIBUTES 模式下出现的
 * 
 * 当 test-client.js 中的 CONCURRENT_REQUESTS（并发控制）为 4 且 监听 3000 端口的线程数为 4 时，test-client 控制台输出每个线程处理了 5 个请求
 * 
 * 当 test-client.js 中的 CONCURRENT_REQUESTS（并发控制）为 5 且 监听 3000 端口的线程数为 4 时，test-client 执行 4 次，控制台输出 3 个线程处理了 4 个请求，一个线程处理了 8 个请求
 * 因为test-client.js中的并发控制是 5，而监听 3000 端口的线程是 4 个，所以总是差 1 个，currentWorker 又是全局的，不是每次都重置的，所以出现这个问题
 * 
 * 当 test-client.js 中的 CONCURRENT_REQUESTS（并发控制）为 1 且 监听 3000 端口的线程数为 4 时，test-client 控制台输出 1 个线程处理了 20 个请求，是因为 Keep-Alive 复用连接，这是正常的
 * */ 

/**
 * Node.js集群实现 - 主进程
 * 支持两种分发模式：操作系统分发和主进程手动轮询分发
 * 支持多端口监听：偶数进程监听一个端口，奇数进程监听另一个端口
 */

const http = require('http');
const net = require('net');
const os = require('os');
const { fork } = require('child_process');
const path = require('path');

// 配置
const PORT1 = 3000;
const PORT2 = 3001;
const numCPUs = os.cpus().length;

// 存储工作进程
const workers = [];

// 轮询计数器
let currentWorker = 0;
let activeConnections = 0;

// 分发请求的两种模式
const DISTRIBUTION_MODE = {
  MASTER_ACCEPTS_THEN_DISTRIBUTES: 'master', // 主进程接受连接后分发（早期实现）
  OS_DISTRIBUTES_DIRECTLY: 'os'              // 操作系统直接分发（现代实现）
};

// 选择分发模式
const distributionMode = DISTRIBUTION_MODE.MASTER_ACCEPTS_THEN_DISTRIBUTES;

console.log(`主进程 ${process.pid} 启动`);
console.log(`CPU核心数: ${numCPUs}`);
console.log(`使用分发模式: ${distributionMode}`);
console.log(`端口配置: 偶数进程监听 ${PORT1}，奇数进程监听 ${PORT2}`);

// 初始化系统
function init() {
  // 创建工作进程
  forkWorkers();
  
  if (distributionMode === DISTRIBUTION_MODE.OS_DISTRIBUTES_DIRECTLY) {
    // 创建两个HTTP服务器，分别监听不同端口
    const server1 = http.createServer();
    const server2 = http.createServer();
    
    // 分别让两个服务器监听不同端口
    server1.listen(PORT1, () => {
      console.log(`主服务器1在端口 ${PORT1} 上监听（操作系统分发模式）`);
      
      // 将服务器句柄传递给偶数工作进程
      for (let i = 0; i < workers.length; i++) {
        if (i % 2 === 0) { // 偶数进程
          workers[i].send('server', server1);
        }
      }
    });
    
    server2.listen(PORT2, () => {
      console.log(`主服务器2在端口 ${PORT2} 上监听（操作系统分发模式）`);
      
      // 将服务器句柄传递给奇数工作进程
      for (let i = 0; i < workers.length; i++) {
        if (i % 2 !== 0) { // 奇数进程
          workers[i].send('server', server2);
        }
      }
    });
    
    // 处理服务器关闭
    process.on('SIGINT', () => {
      console.log('正在关闭服务器...');
      Promise.all([
        new Promise(resolve => server1.close(resolve)),
        new Promise(resolve => server2.close(resolve))
      ]).then(() => {
        console.log('所有服务器已关闭。');
        workers.forEach(worker => worker.kill());
        process.exit(0);
      });
    });
  } else {
    // 创建两个TCP服务器以实现手动分发
    const server1 = net.createServer({ pauseOnConnect: true });
    const server2 = net.createServer({ pauseOnConnect: true });
    
    // 为两个服务器分别设置连接处理
    setupConnectionHandler(server1, 0); // 偶数工作进程处理
    setupConnectionHandler(server2, 1); // 奇数工作进程处理
    
    // 分别监听两个端口
    server1.listen(PORT1, () => {
      console.log(`主服务器1在端口 ${PORT1} 上监听（手动轮询分发模式）`);
    });
    
    server2.listen(PORT2, () => {
      console.log(`主服务器2在端口 ${PORT2} 上监听（手动轮询分发模式）`);
    });
    
    // 处理服务器关闭
    process.on('SIGINT', () => {
      console.log('正在关闭服务器...');
      Promise.all([
        new Promise(resolve => server1.close(resolve)),
        new Promise(resolve => server2.close(resolve))
      ]).then(() => {
        console.log('所有服务器已关闭。');
        workers.forEach(worker => worker.kill());
        process.exit(0);
      });
    });
  }
}

// 设置连接处理器（仅用于手动轮询模式）
function setupConnectionHandler(server, startWorkerIndex) {

  server.on('connection', (socket) => {
    activeConnections++;
    // console.log(`主进程: 收到新连接，当前活跃连接: ${activeConnections}`);
    
    // 保持连接
    socket.setKeepAlive(true);
    
    // 暂停读取数据
    socket.pause();
    
    // 找到可用的工作进程（按奇偶性选择）
    const availableWorkers = workers.filter((_, index) => index % 2 === startWorkerIndex);
    
    // 在可用工作进程中轮询
    let workerIndex = currentWorker % availableWorkers.length;
    const worker = availableWorkers[workerIndex];
    
    // 更新轮询计数器
    currentWorker++;
    
    // 将连接传递给工作进程
    worker.send('connection', socket);
    console.log(`主进程: 将连接分发给工作进程 ${worker.pid}`);
    
    // 监听socket关闭
    socket.on('close', () => {
      activeConnections--;
      // console.log(`主进程: 连接关闭，剩余活跃连接: ${activeConnections}`);
    });
  });
}

// 创建工作进程
function forkWorkers() {
  for (let i = 0; i < numCPUs; i++) {
    createWorker(i);
  }
  
  console.log(`已创建 ${workers.length} 个工作进程`);
}

// 创建单个工作进程
function createWorker(index) {
  const worker = fork(path.join(__dirname, 'worker.js'));
  workers.push(worker);
  
  // 发送进程索引信息
  worker.send({ type: 'init', index: index });
  
  // 监听工作进程消息
  worker.on('message', (message) => {
    if (message.type === 'ready') {
      // 确定此进程监听的端口
      const port = index % 2 === 0 ? PORT1 : PORT2;
      console.log(`工作进程 ${worker.pid} 准备就绪，将监听端口 ${port}`);
    } else if (message.type === 'request_completed') {
      console.log(`主进程: 工作进程 ${worker.pid} 完成请求处理: ${message.data.url}`);
    } else if (message.type === 'error') {
      console.error(`主进程: 工作进程 ${worker.pid} 报告错误:`, message.data.message);
    } else if (message.type === 'request_received') {
      // 可选：记录请求接收情况
    } else {
      console.log(`来自工作进程 ${worker.pid} 的消息:`, message);
    }
  });
  
  // 监听工作进程退出
  worker.on('exit', (code, signal) => {
    console.log(`工作进程 ${worker.pid} 退出，代码: ${code}, 信号: ${signal}`);
    
    // 从列表中移除
    const workerIndex = workers.indexOf(worker);
    if (workerIndex !== -1) {
      const index = workerIndex; // 保存原始索引
      workers.splice(workerIndex, 1);
      
      // 创建新工作进程，保持相同索引
      createWorker(index);
    }
  });
  
  return worker;
}

// 处理未捕获异常
process.on('uncaughtException', (err) => {
  console.error('主进程发生未捕获的异常:', err);
});

// 启动系统
init();